# Учебный проект: Лента-Меню

Создайте класс `RibbonMenu`, описывающий компонент "Ленты-Меню"(для простоты будем называть его "меню"). Данный компонент представляет из себя список категорий товаров ресторана. В конечном итоге, мы будем показывать товары только той категории, которую выбрал пользователь.

В качестве аргумента в конструктор класса передаётся массив категорий:

```js
let categories = [
  {
    id: '',
    name: 'All'
  },
  {
    id: 'salads', // уникальный идентификатор категории
    name: 'Salads' // имя категории для отображения
  },
  {
    id: 'soups',
    name: 'Soups'
  },
  // и другие...
];

let ribbonMenu = new RibbonMenu(categories);
```

После этого в `ribbonMenu.elem` должен быть доступен корневой DOM-элемент меню. 

На вёрстку можно посмотреть в файле `static.html`, а пример использования - в файле `index.html`.

## Отрисовка вёрстки

Как видно из вёрстки готового меню в файле `static.html`:
- корневой элемент компонента имеет класс `ribbon` 
- ссылки на категории находятся внутри элемента с классом `ribbon__inner`
- вёрстка ссылки на категорию выглядит вот так:
```html
<a href="#" class="ribbon__item" data-id="salads">Salads</a>
```

## Прокрутка меню

Как видно из примера, ссылки категорий занимают больше места, чем общая ширина меню. Поэтому, чтобы пользователь мог выбрать категории, которые не помещаются на экране, мы будем прокручивать меню по горизонтали.

Меню должно прокручиваться влево/вправо при клике по кнопкам вперёд/назад.

Их CSS классы:
- `ribbon__arrow_right` - класс кнопки прокрутки вперёд;
- `ribbon__arrow_left` - класс кнопки прокрутки назад;

### Как (технически) будет устроена прокрутка?

Структура меню такова, что есть внешний элемент, в котором находится "лента" из подряд идущих ссылок на категории. Внешний элемент имеет фиксированную ширину, поэтому видно только часть ленты.

Класс элемента-ленты, в котором находятся все категории - `ribbon__inner`. Прокручивать его мы будем с шагом в `350px`, и делать это c помощью встроенного метода `scrollBy`.

Например, чтобы прокрутить его на `350px` вперёд:
```js
ribbonInner.scrollBy(350, 0); // положительное значение для прокрутки назад
```

А, чтобы прокрутить его на `350px` назад:
```js
ribbonInner.scrollBy(-350, 0); // отрицательное значение для прокрутки назад
```

Подробнее про метод `scrollBy` можно прочитать в статье - [Размеры и прокрутка окна](https://learn.javascript.ru/size-and-scroll-window#window-scroll)

### Скрываем кнопки переключения при достижении крайних положений меню

Когда пользователь прокрутил меню до крайней категории, нужно скрыть кнопку переключения вперёд, и наоборот, в начальном состоянии меню, нужно скрыть кнопку переключения назад. 

Скрывать и показывать кнопки нужно с помощью класса `ribbon__arrow_visible`, если этот класс есть у кнопки, она будет видима, и наоборот. Для добавления и удаления можно использовать свойство `classList`.

Важный момент, прокрутка - это асинхронное действие, она занимает какое-то время, а не происходит моментально. Поэтому скрывать кнопки нужно, когда прокрутка закончена - в обработчике события `scroll` на элементе с классом `ribbon__inner`, а не сразу после вызова метода `scrollBy`.

**Но как определить текущее состояние прокрутки?**

Для этого нам нужно вычислить ширину оставшейся невидимой области.

Для области скрытой `слева`, всё очень просто, мы можем использовать свойство `scrollLeft` элемента с классом `ribbon__inner`:

```js
let scrollLeft = ribbonInner.scrollLeft;

console.log(scrollLeft); // число пикселей, например, 100 или 0.

```

Другими словами, если `scrollLeft` будет равен `0`, значит мы "упёрлись" в первый слайд и нужно скрыть кнопку назад.

Для области скрытой `справа` всё немного сложнее. Чтобы её вычислить нам нужно взять общую ширину прокрутки элемента(свойство `scrollWidth`) и вычесть из неё ширину области скрытой слева(свойство `scrollLeft`) и видимую ширину элемента (свойство `clientWidth`):

```js
let scrollWidth = ribbonInner.scrollWidth;
let scrollLeft = ribbonInner.scrollLeft;
let clientWidth = ribbonInner.clientWidth;

let scrollRight = scrollWidth - scrollLeft - clientWidth; // число пикселей, например, 100 или 0.
```

Аналогично, если `scrollRight` будет равен `0`, значит мы "упёрлись" в крайний слайд и нужно скрыть кнопку вперёд.
В некоторых браузерах у вас может получиться дробное значение `scrollRight`. Чтобы себя обезопасить, все значения `scrollRight` от 0 до 1 нужно считать нулем. Другими словами, если `scrollRight < 1`, то мы можем смело скрывать кнопку переключения вперёд.

Подробнее про эти свойства и не только можно прочитать в статье - [Размеры и прокрутка окна](https://learn.javascript.ru/size-and-scroll-window)

## Выбор конкретной категории

Для выбора конкретной категории товаров пользователю нужно будет кликнуть по её ссылке. После чего, нам будет нужно:

Во-первых, остановить действия браузера по умолчанию, вызвав метод `preventDefault` на объекте события.

Во-вторых, выделить стилями, выбранную категорию, добавив ссылке класс `ribbon__item_active`. При этом нужно не забыть удалить этот класс с предыдущей активной категории.

В-третьих, дать знать другим компонентам, какую категорию выбрал пользователь. Для этого сгенерировать пользовательское событие на корневом HTML элементе компонента (который хранится в свойстве `elem`), такого вида: 

```js
new CustomEvent('ribbon-select', { // имя события должно быть именно 'ribbon-select'
  detail: category.id, // уникальный идентификатора категории из её объекта
  bubbles: true // это событие всплывает - это понадобится в дальнейшем
})
```

Как вы видите, для генерации такого события необходим уникальный идентификатор категории (`category.id`). Для простоты его можно хранить в дата-атрибуте, к примеру, мы используем атрибут `data-id` на элементе ссылки.

Про пользовательские события можно прочитать в статье - [Генерация пользовательских событий](https://learn.javascript.ru/dispatch-events).
